package org.frank.blockchain.p2p.client;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.*;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.SneakyThrows;
import org.frank.blockchain.enums.CommandType;
import org.frank.blockchain.domain.Command;
import org.frank.blockchain.p2p.server.CommandDecoder;
import org.frank.blockchain.util.Constants;

import java.net.InetSocketAddress;
import java.util.UUID;

public class P2pClient {
    Channel channel;
    EventLoopGroup workerGroup;

    @SneakyThrows
    public void start(String host, int port) {
        workerGroup = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap(); // (1)
        b.group(workerGroup); // (2)
        b.channel(NioSocketChannel.class); // (3)
        b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
        b.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new CommandDecoder(), new P2pClientHandler());
            }
        });

        // 启动客户端
        channel = b.connect(host, port).sync().channel(); // (5)
        int clientPort = ((InetSocketAddress)channel.localAddress()).getPort();
        System.out.println(clientPort);
//        b.bind(9010).sync();
        // 等待连接关闭
//            f.channel().closeFuture().sync();
    }

    public void stop() {
        if (ObjectUtil.isNotEmpty(workerGroup)) {
            workerGroup.shutdownGracefully();
        }
    }

    @SneakyThrows
    public Command call(Command command) {
        command.setType(CommandType.Request);
        String json = JSONUtil.toJsonStr(command);
        json += Constants.END_CHAR;
        ChannelFuture future = channel.writeAndFlush(Unpooled.wrappedBuffer(json.getBytes()));
        future.sync();
        P2pClientHandler handler = channel.pipeline().get(P2pClientHandler.class);
        Command response = handler.getResponse(command.getRequestId());
        if (!response.success) {
            throw new Exception(response.errorMessage);
        }
        return response;
    }
}
